/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package algorithm;

import java.util.ArrayList;

/**
 * The Reorganizer class should be used to reorganize the tasks after a two
 * tasks were switched. It uses a part of the GlouGlouAlgo algorithm
 *
 * @author Lu
 */
public class Reorganizer {

    private int[] ressourcesCumulatedTime;
    private ArrayList<WorkingTask> currentState;
    private ArrayList<ArrayList<WorkingTask>> tasks;
    private int ressources;
    private Solution newSolution;

    public Reorganizer(int ressources) {
        this.ressources = ressources;
        ressourcesCumulatedTime = new int[ressources];
        currentState = new ArrayList<>();
    }

    public Solution computeNewSolution(Solution unorganizedSolution) {
        // We get the assigned task from the solution
        tasks = unorganizedSolution.getSolution();
        // We create the instance for the new corrected solution
        newSolution = new Solution(unorganizedSolution.getNumberOfResources());
        ArrayList<ArrayList<WorkingTask>> newSolutionArray = new ArrayList<>();
        for (int i = 0; i < unorganizedSolution.getNumberOfResources(); i++) {
            newSolutionArray.add(new ArrayList<WorkingTask>());
        }
        // We will save the index of the last task we added on each ressource
        int ressourcesIndex[] = new int[unorganizedSolution.getNumberOfResources()];
        // Current ressource used, we get the lowest
        int currentRessource = getLowestRessourceTime();
        // The current time
        int currentTime = 0;
        // The current task chosen
        WorkingTask currentTask = tasks.get(currentRessource).get(ressourcesIndex[currentRessource]);
        // We take it out
        if (!tasks.get(currentRessource).remove(currentTask)) {
            System.out.println("Could not remove task.");
        }
        // We look for a suitable task
        while (currentTask != null) {
            // If the task cannot be added at the current time, we wait until its ressource and machine are free
            if (isBlockedTask(currentTask)) {
                int w = waitUntilRessourceIsFree(currentRessource, newSolutionArray);
                if (w != Integer.MAX_VALUE) {
                    currentTime = w;
                }
            }
            int length = currentTask.getEndTime() - currentTask.getStartTime();
            currentTask.setStarttime(ressourcesCumulatedTime[currentRessource]);
            currentTask.setEndtime(ressourcesCumulatedTime[currentRessource] + length);
            ressourcesCumulatedTime[currentRessource] = currentTask.getEndTime();
            WorkingTask taskToAdd = new WorkingTask(currentTask.getStartTime(), currentTask.getEndTime(), 
                    currentTask.getMachineUsed(), currentTask.getProductUsed(), currentRessource, currentTask.getTask());
            newSolutionArray.get(currentRessource).add(taskToAdd);
            currentState.add(currentTask);
            // update currentTime
            currentTime = Integer.MAX_VALUE;
            for (int i = 0; i < ressourcesCumulatedTime.length; i++) {
                if (currentTime > ressourcesCumulatedTime[i]) {
                    currentTime = ressourcesCumulatedTime[i];
                }
            }
            for (int i = currentState.size() - 1; i >= 0; i--) {
                if (currentState.get(i).getEndTime() <= currentTime) {
                    currentState.remove(i);
                }
            }
            // We select the ressource with the lowest time used
            currentRessource = getLowestRessourceTime();
            // We select the next task to add
            if (currentRessource != -1) {
                currentTask = tasks.get(currentRessource).get(ressourcesIndex[currentRessource]);
                if (!tasks.get(currentRessource).remove(currentTask)) {
                    System.out.println("Could not remove task.");
                }
            } else {
                currentTask = null;
            }
        }
        newSolution.setSolution(newSolutionArray);
        return newSolution;
    }

    private int waitUntilRessourceIsFree(int currentRessource, ArrayList<ArrayList<WorkingTask>> newSolutionArray) {
        int newTime = Integer.MAX_VALUE;
        for (int i = 0; i < newSolutionArray.size(); i++) {
            // if this isn't the current ressource and this is the minimum cumuleted time
            if (i != currentRessource && (ressourcesCumulatedTime[i]) < newTime) {
                // then we're going to wait until then
                newTime = ressourcesCumulatedTime[i];
            }
        }
        // we update the current ressource time
        if (newTime != Integer.MAX_VALUE) {
            ressourcesCumulatedTime[currentRessource] = newTime;
        }
        return newTime;
    }

    private boolean isBlockedTask(WorkingTask currentTask) {
        for (WorkingTask w : currentState) {
            if (currentTask.getMachineUsed() == w.getMachineUsed() || currentTask.getProductUsed() == w.getProductUsed()) {
                if (w.getEndTime() > ressourcesCumulatedTime[currentTask.getRessourceUsed()]) {
                    return true;
                }
            }
        }
        return false;
    }

    private int getLowestRessourceTime() {
        int lowest = Integer.MAX_VALUE;
        int index = -1;
        for (int i = 0; i < ressources; i++) {
            if (lowest > ressourcesCumulatedTime[i]) {
                if (!tasks.get(i).isEmpty()) {
                    lowest = ressourcesCumulatedTime[i];
                    index = i;
                }
            }
        }
        return index;
    }
}
